#include "AManOfMapsState.h"
#include "mapscreenState.h"

AManOfMapsState AManOfMapsState::m_AManOfMapsState;
b2World* AManOfMapsState::world;
std::vector<TestEntity*> AManOfMapsState::EntityList;
int AManOfMapsState::currentMap;
std::vector<TileMap*>AManOfMapsState::mapList;

void AManOfMapsState::Init(sf::RenderWindow& window){

    std::cout << "Start MoM State..." << std::endl;

    //B2 World Init
    timeStep = 1/30.0;      //the length of time passed to simulate (seconds)
    velocityIterations = 8;   //how strongly to correct velocity
    positionIterations = 3;   //how strongly to correct position
    b2Vec2 GRAVITY(0.0, 0.0);   //Gravity off
    world = new b2World(GRAVITY);

    // +++ TGUI Stuff +++
    gui.setWindow(window);  //create the tGUI window to the window
    gui.setGlobalFont("resources/arial.ttf");   //set the text font

    //Buttons
    tgui::Button::Ptr button2(gui,"button2");

    if(!button2->load("resources/Black.conf")) std::cout << "Texture did not load";
    button2->setSize(window.getSize().x * 0.1 , window.getSize().y * 0.05);
    button2->setPosition(window.getSize().x *.85 , window.getSize().y * 0.7);
    button2->setText("Explore");
    button2->bindCallback(tgui::Button::LeftMouseClicked);
    button2->setCallbackId(1);
    button2->setTextColor(sf::Color::White);

    tgui::Button::Ptr bCamp = gui.copy(button2, "bCamp");
    bCamp->setText("Camp");
    bCamp->setCallbackId(2);
    bCamp->setPosition(window.getSize().x *.85 , window.getSize().y *0.8);

    tgui::Button::Ptr bCloser = gui.copy(button2, "bCloser");
    bCloser->setText("Look Closer");
    bCloser->setCallbackId(3);
    bCloser->setPosition(window.getSize().x *.85 , window.getSize().y *0.9);

    tgui::TextBox::Ptr textBox(gui, "tBox1");
    textBox->load("resources/Black.conf");
    textBox->setPosition(window.getSize().x * 0.39, window.getSize().y * 0.81);
    textBox->setSize(window.getSize().x * 0.4, window.getSize().y * 0.18);
    textBox->setTextSize(16);
    textBox->setReadOnly(true);
    textBox->setText("Left click to Move");

    //Load the required texture for the maps
    std::cout << "Loading Resources... " << std::endl;
    load("resources/man_of_maps.png");

    // +++ TILEMAP +++
    currentMap = 0;
    makeMap(50,50);    //make map 0 (already set as currentMap default)
    makeMap(50,50);     //make map 1
    makeMap(100,30);    //make map 2
    makeMap(100,100);     //make map 3

    //New Player
    player = new Player(world, BOX, sf::Vector2f(0,0), "resources/mom_player.png");
    player->toggleAnimated();
    player->setDimensions(.5, .5);
    player->setCurrentFrame(0);

    EntityList.push_back(player);

    //Test Box and Wall Entity

    TestEntity* t;
    t = new TestEntity(world, EDGE,sf::Vector2f(900,1000),"resources/noah.png");
    EntityList.push_back(t);
    for(int i = 0; i < 10; i++){
        t = new TestEntity(world, BOX,sf::Vector2f(1200 + i * 10 ,1000+i*50),"resources/box.png");
        EntityList.push_back(t);
    }



    //Wall Entity
    EntityList.push_back(new TestEntity(world, EDGE, sf::Vector2f(1400 ,1200), sf::Vector2f(150,20)));

    // +++ VIEWS +++
    view.setSize(window.getSize().x, window.getSize().y);
    std::cout << "Window Size(px): " << window.getSize().x << " " << window.getSize().y << std::endl;
    view.zoom(1.0); //why?
    view.setViewport(sf::FloatRect(0,0,1.0,1.0));
    window.setView(view);
    cameraCentered = true;

    //button view
    view2.setCenter(window.getSize().x / 2, window.getSize().y / 2);
    view2.setSize(window.getSize().x, window.getSize().y);

    //initialize the player and camera
    //NOTE: this could be a function relating to player setup. Used also in resume()
    resetPlayer();

    // Time of Day Clock
    time.resize(2);
    time[0] = 12;
    time[1] =0;

    // Text
    font.loadFromFile("resources/arial.ttf");
    std::string str = Utility::toString(time[0]) + ":" + Utility::toString(time[1]) + "0" + " Pm";
    text.setString(str);
    text.setFont(font);
    text.setCharacterSize(30);
    text.setStyle(sf::Text::Bold);
    text.setColor(sf::Color::Red);
    text.setPosition(500,500);

    // Message Box
    //mBox = new Message();

    sMenu = new sideMenu(window);
    bMenu = new BottomMenu(window);

    energyDrain = 0;

    explore();

}

void AManOfMapsState::Cleanup(){
    std::cout << "A Man of Maps State Closed" << std::endl;
    playerMoveDir = sf::Vector2f(0,0);
}

void AManOfMapsState::Pause(){
    std::cout << "A Man of Maps State Paused" << std::endl;
}

void AManOfMapsState::Resume(){
    std::cout << "A Man of Maps State Restarted" << std::endl;

    //update player position and view only if player walked out of current tilemap
    if(!mapList[player->getMapIndex()]->getWalkBounds().getGlobalBounds().contains(player->getPosition())){
        resetPlayer();
    }
}

void AManOfMapsState::Events(GameEngine* game, sf::RenderWindow& window){

    while (window.pollEvent(event)){

        // Close the window and end the game when X clicked
        if (event.type == sf::Event::Closed){
            game->Quit();
        }

        //handle tGUI events
        gui.handleEvent(event);

        // The callback loop
        tgui::Callback callback;

        while (gui.pollCallback(callback)){
            // Change the tile where the player is standing
            if (callback.id == 1){
                std::cout << "Button 2 Pressed - Explore" << std::endl;
                explore();
            }

            if (callback.id == 2){
                std::cout << "Button 3 Pressed - Camp" << std::endl;
                camp();
            }

            if (callback.id == 3){
                std::cout << "Button 3 Pressed - Look Closer" << std::endl;
                lookCloser();
            }

        }

        // === KEYBOARD EVENTS ===
        if (event.type == sf::Event::KeyPressed && !player->isMoving() ){

            //RETURN KEY
            if (event.key.code == sf::Keyboard::Return){
                explore();
                //mBox->toggleState();
            }

            //ESCAPE KEY
            if (event.key.code == sf::Keyboard::Escape){
                game->PushState( mapscreenState::Instance());
            }

            //Move Keys
            /*
            if (event.key.code == sf::Keyboard::D || event.key.code == sf::Keyboard::A  || event.key.code == sf::Keyboard::W || event.key.code == sf::Keyboard::S){
                if (event.key.code == sf::Keyboard::W) playerMoveDir = sf::Vector2f(0,-1);
                if (event.key.code == sf::Keyboard::A) playerMoveDir = sf::Vector2f(-1,0);
                if (event.key.code == sf::Keyboard::S) playerMoveDir = sf::Vector2f(0,1);
                if (event.key.code == sf::Keyboard::D) playerMoveDir = sf::Vector2f(1,0);

                float cost = moveCost(playerMoveDir);


                sf::Vector2f target = sf::Vector2f(playerMoveDir.x * tileSize + player->getPosition().x,
                                                         playerMoveDir.y * tileSize + player->getPosition().y);


                //Move player if he meets the cost and the tile he wants to move to is unexplored
                if(player->getEnergy() >= cost){
                    bool okToMove = mapList[player->getMapIndex()]->isExplored(player->getPosition()) || mapList[player->getMapIndex()]->isExplored(target);
                    if(okToMove){
                            energyDrain += cost;
                    }else{
                        playerMoveDir = sf::Vector2f(0,0);
                        updateMessage("That way's too dangerous. Explore here or go back.");
                    }
                }else{
                    playerMoveDir = sf::Vector2f(0,0);
                    updateMessage("Not enough energy to move there!");
                }
            }
            */
        }


        // === SINGLE MOUSE EVENTS ===
        if (event.type == sf::Event::MouseButtonPressed){
            //When the player left clicks, set the direction of the player based on the line between current position and target point
            if(event.mouseButton.button == sf::Mouse::Left){

                /*
                window.setView(view);

                //limit clicking to inside the first window
                sf::FloatRect r = sf::FloatRect(0,0,view.getViewport().width * window.getSize().x,
                                                 view.getViewport().height * window.getSize().y);

                if(r.contains(sf::Vector2f(sf::Mouse::getPosition(window)))){
                    sf::Vector2f targetPos = window.mapPixelToCoords(sf::Mouse::getPosition(window));
                    moveDistance = Utility::getDistance(player->getPosition().x, player->getPosition().y, targetPos.x, targetPos.y);
                    //std::cout << "Distance: " << moveDistance << std::endl;
                    playerMoveDir = sf::Vector2f(targetPos.x- player->getPosition().x, targetPos.y - player->getPosition().y);
                    //normalize playerMoveDir
                    float denom = sqrt((playerMoveDir.x * playerMoveDir.x) + (playerMoveDir.y * playerMoveDir.y));
                    playerMoveDir.x = playerMoveDir.x / denom;
                    playerMoveDir.y = playerMoveDir.y / denom;
                }
                */
            }

            if(event.mouseButton.button == sf::Mouse::Right){

                testRect.setPosition(200,200);
                testRect.setSize(sf::Vector2f(200,200));
                testRect.setOutlineThickness(2);
                testRect.setFillColor(sf::Color::Transparent);
                testRect.setOutlineColor(sf::Color::Blue);

                //lights.push_back(window.mapPixelToCoords(sf::Mouse::getPosition(window), view));
                //makeTestArray(window);
                //std::cout << "Xpixel: " << pixel.x << " " << pixel.y << " Coords: " << coords.x << " " << coords.y << std::endl;
                //int targetTile = mapList[currentMap]->getTileNumber(targetPos);
                //std::cout << "Tile: " << targetTile << std::endl;
                //sf::FloatRect r = mapList[currentMap]->getTileRect(targetTile);
                //makeTestArray(window, sf::Vector2f(r.left, r.top));

            }




        }
    }


        // === CONTINUOUS MOUSE EVENTS ===

        if (sf::Mouse::isButtonPressed(sf::Mouse::Left))
        {
                window.setView(view);

                //limit clicking to inside the first window
                sf::FloatRect r = sf::FloatRect(0,0,view.getViewport().width * window.getSize().x,
                                                 view.getViewport().height * window.getSize().y);

                //Set the move direction and distance
                if(r.contains(sf::Vector2f(sf::Mouse::getPosition(window)))){
                    sf::Vector2f targetPos = window.mapPixelToCoords(sf::Mouse::getPosition(window));
                    moveDistance = Utility::getDistance(player->getPosition().x, player->getPosition().y, targetPos.x, targetPos.y);
                    //std::cout << "Distance: " << moveDistance << std::endl;
                    playerMoveDir = sf::Vector2f(targetPos.x- player->getPosition().x, targetPos.y - player->getPosition().y);
                    //normalize playerMoveDir
                    float denom = sqrt((playerMoveDir.x * playerMoveDir.x) + (playerMoveDir.y * playerMoveDir.y));
                    playerMoveDir.x = playerMoveDir.x / denom;
                    playerMoveDir.y = playerMoveDir.y / denom;
                }
        }




        //Zoom view in and out using the mouse scrollwheel
        if (event.type == sf::Event::MouseWheelMoved){

            if(event.mouseWheel.delta == 1){
                view.zoom(0.999f);
            }else if (event.mouseWheel.delta == -1){
                view.zoom(1.001f);
            }
            std::cout  << "Window Size(px): " << view.getSize().x << " " << view.getSize().y << std::endl;

        }

    }

void AManOfMapsState::Update(GameEngine* game, sf::RenderWindow& window){

        //Start the player moving (player may already be moving, that's ok)
        if((playerMoveDir.x !=0 || playerMoveDir.y !=0)){
            //std::cout << "Should only be called once per press" << std::endl;
            player->startMoving(b2Vec2(playerMoveDir.x,playerMoveDir.y));
            playerMoveDir = sf::Vector2f(0,0);
        }

        //currently stopping player based on distance moved.
        if(player->isMoving()){
            if(player->getDistanceMoved() > moveDistance){
                player->stopMoving();
            }
            centerCamera();
            removeFog();
            makeTestArray(window);
       }


        //Update Player Energy
        if(energyDrain > 0){
            player->setEnergy(player->getEnergy()- energyDrain);
            bMenu->Update(player);
            energyDrain = 0;
        }

        //advance the world
        world->Step(timeStep, velocityIterations, positionIterations);

        //Update all entitites
        for(unsigned int i = 0;i != EntityList.size();i++) {
            TestEntity* t = EntityList[i];
            t->Update();
        }

        // TEST Open Map Menu if the player exits the WalkBounds (box for each map where player leaves map)
        if(!mapList[player->getMapIndex()]->getWalkBounds().getGlobalBounds().contains(player->getPosition())){
           game->PushState(mapscreenState::Instance());
           }

        //Raytrace Stuff
        /*
        sf::Vector2i play = window.mapCoordsToPixel(player->getPosition(), view);
        sf::Vector2i m = sf::Mouse::getPosition(window);
        testPoints.setPrimitiveType(sf::Points);
        testPoints.clear();
        //repeat raytrace for all points in the points list
        raytrace(play.x, play.y, m.x,m.y);
        */

}

void AManOfMapsState::Render(GameEngine* game, sf::RenderWindow& window){

    window.setView(view);

    window.clear(sf::Color::White);

    //draw only the current map
    window.draw(*(mapList[currentMap]));

    //draw all entities
    for(unsigned int i = 0;i < EntityList.size();i++) {
        EntityList[i]->Render(window);
    }

    // TEST - render the walk bounds rectangle
    window.draw(mapList[player->getMapIndex()]->getWalkBounds());

    window.setView(view2);

    window.draw(text);

    //mBox->Render(window);

    //sMenu->Render(window);
    //bMenu->Render(window);

    gui.draw();

    //Update the vertex Arrays
    window.draw(testArray1);

    //Draw the ray trace stuff
    window.draw(testPoints);
    window.draw(testRect);

    window.display();
}

//Remove the fog around the player
void AManOfMapsState::removeFog(){

    //int currentMap = player->getMapIndex(); //figure out what tile # the player is on
    int layer = 2;  //set the layer to fog (2)
    unsigned int start = mapList[currentMap]->getTileNumber(sf::Vector2f(getViewRect().left, getViewRect().top));
    unsigned int stop = mapList[currentMap]->getTileNumber(sf::Vector2f(getViewRect().left + getViewRect().width,
                                                                               getViewRect().top + getViewRect().height));
    //std::cout << "Stop#/Start#: " << stop << "/" << start << " Total: " << stop - start << std::endl;
    for(unsigned int i = start; i <= stop; i++ ){
        if(i < mapList[currentMap]->getTotalTiles()){
            sf::FloatRect rect1(mapList[currentMap]->getTileRect(i));   //rect of tile
            sf::FloatRect rect2(player->getRect().getGlobalBounds());   //rect of player
            if(rect2.intersects(rect1)){
                mapList[currentMap]->setLayerTexture(i,0,layer,0);  //remove fog on this tile
            }
        }
    }

    //Smooth the map
    for(unsigned int i = start ; i < stop; i++){
        //Check only nonblank tiles in the fog layer
        if(mapList[currentMap]->getTileType(i,layer) != 0){
            mapList[currentMap]->smoothEdges(i,1,2,2);   //1 corresponds to the Tilemap ID for solid fog
        }
    }
}


void AManOfMapsState::centerCamera(){
//Centers the camera over the player if the player is not near map edges
//Does not correct camera if player has moved, but is not near eges (use resetCameraToPlayer)
    //std::cout << "Run Center Camera" << std::endl;

    float tempWidth = mapList[player->getMapIndex()]->getWorldDimensions().x ;
    float tempHeight = mapList[player->getMapIndex()]->getWorldDimensions().y ;
    mapRectangle.setSize(sf::Vector2f(tempWidth,tempHeight));   //set the size of the test rectangle to the current map size
    mapRectangle.setPosition(0,0);
    //mapRectangle.setFillColor(sf::Color::Transparent);
    //mapRectangle.setOutlineColor(sf::Color::Red);
    //mapRectangle.setOutlineThickness(1);

    sf::FloatRect mapRect = mapRectangle.getGlobalBounds();
    sf::FloatRect viewRect = getViewRect();

    //Check for intersection
    float tempCenterX;
    float tempCenterY;

    //Set X to current camera X if view rect is out of the map, otherwise, center on player
    if(viewRect.left <= mapRect.left || (viewRect.left + viewRect.width) >= (mapRect.left + mapRect.width) ){
        tempCenterX = view.getCenter().x;
    }else{
        tempCenterX = player->getPosition().x;
    }
    //Set Y
    if(viewRect.top <= mapRect.top || (viewRect.top + viewRect.height) >= (mapRect.top + mapRect.height) ){
        tempCenterY = view.getCenter().y;
    }else{
        tempCenterY = player->getPosition().y;
    }

    view.setCenter(tempCenterX, tempCenterY);
}

bool AManOfMapsState::load(const std::string& filename){

    if (!m_tileset.loadFromFile(filename)) return false;

    return true;
}

//returns the rect associated with the current view in world coordinates
sf::FloatRect AManOfMapsState::getViewRect(){
        float viewX = player->getPosition().x - (view.getSize().x / 2);
        float viewY = player->getPosition().y - (view.getSize().y / 2);
        return sf::FloatRect(viewX, viewY, view.getSize().x, view.getSize().y);
        //return view.getViewport();
}

//All activities involved in exploring a tile
void AManOfMapsState::explore(){

    int exploreCost = 10;

    //check for available energy and whether the tile has already been explored.
    if(!mapList[player->getMapIndex()]->isExplored(player->getPosition())){
        if(player->getEnergy() >= exploreCost){
            std::cout << "exploring" << std::endl;
            removeFog();    //remove the fog around the explored tile
            mapList[player->getMapIndex()]->exploreTile(player->getPosition()); //set the tile to explored
            energyDrain += exploreCost;
        }else{
            updateMessage("Not enough energy to Explore! ");

        }
    }else{
         updateMessage("This area is already explored!");
    }
}

void AManOfMapsState::updateMessage(sf::String str){
    tgui::TextBox::Ptr test = gui.get("tBox1");
    test->addText("\n" + str + "\n");
}

//takes in the intended direction, returns cost of move based on whether the tile is explored
float AManOfMapsState::moveCost(sf::Vector2f vec){

    int moveCost = 2;

    sf::Vector2f testPosition = sf::Vector2f(vec.x * tileSize + player->getPosition().x,
                                             vec.y * tileSize + player->getPosition().y);

    //determine if target tile is explored
    if(mapList[player->getMapIndex()]->isExplored(testPosition)){
        return 0;
    }else return moveCost;

}

//Camp the player on explored tile
void AManOfMapsState::camp(){

    int campCost = 20;

    if(player->getSupplies() >= campCost){
        if(mapList[player->getMapIndex()]->isExplored(player->getPosition())){
            if(player->getEnergy() == player->getMaxEnergy()){
                updateMessage("There's no need to camp now. Plenty of Energy.");
            }else{
                updateMessage("Camping...");
                player->setEnergy(player->getEnergy() + player->getMaxEnergy() / 2);
                player->setSupplies(player->getSupplies()- campCost);
                bMenu->changeDay(1);
                bMenu->Update(player);
            }
        }else updateMessage("It's not safe to camp here");
    }else {
        updateMessage("Not enough supplies.");
    }
}

void AManOfMapsState::lookCloser(){
    updateMessage("Looking Closer");
}

void AManOfMapsState::changeTime(){
    std::cout << "Time Updated: " << std::endl;
}

/*
Creates a map and adds it to the maplist
*/
void AManOfMapsState::makeMap(int width, int height){
    sf::Clock tempClock;
    tempClock.restart();
    sf::Vector2f mapLocation = sf::Vector2f(0,0);           //world location of map top left corner
    sf::Vector2f mapDimensions = sf::Vector2f(width,height);      //size of the map in tiles
    tileSize = 64;  //tile size in px

    //create maps by adding maps to the maplist vector NOTE: The loop is not needed if only one map is visible at a time
    mapList.push_back(new TileMap(tileSize, mapLocation, mapDimensions, &m_tileset));
    std::cout << std::endl << "Created Map in: " << tempClock.getElapsedTime().asSeconds() << "s" << std::endl;
}

// Resets the played on the current map
void AManOfMapsState::resetPlayer(){

        int playerStartTile = mapList[currentMap]->getTotalTiles()/2 + 15;  //ID of the tile where the player starts, needs updated based on size
        std::cout << "Player Start Tile: " << playerStartTile <<  std::endl;

        playerStartPos = mapList[currentMap]->getTileCenter(playerStartTile);
        player->setPosition(playerStartPos);
        view.setCenter(player->getPosition().x, player->getPosition().y);   //centers camera on player position
        player->setCurrentMap(currentMap);

        player->Update();
        playerMoveDir = sf::Vector2f(0,0);
        centerCamera();
}

int AManOfMapsState::getMapListSize(){
    return mapList.size();
}

void AManOfMapsState::makeTestArray(sf::RenderWindow& window){
    //TEST ARRAY
        // populate the vertex array, with one quad per tile i = y , j = x
    int fogTileSize = 20;    //pixel size of the tile
    sf::Vector2f testTileSize = sf::Vector2f(fogTileSize,fogTileSize);
    sf::Vector2f testPosition = sf::Vector2f(0,0);

    int testHeightTiles = 1+ (view.getViewport().height * window.getSize().y) / fogTileSize;
    int testWidthTiles = 1+ (view.getViewport().width * window.getSize().x) / fogTileSize;


    testArray1.setPrimitiveType(sf::Quads);
    testArray1.resize(testHeightTiles * testWidthTiles * 4);

    sf::Clock testClock;
    testClock.restart();

    int distanceLimit = 255;
    int distance;
    int alpha;

    if(lights.size() > 0){
        for (unsigned int i = 0; i < testHeightTiles; ++i){
            for (unsigned int j = 0; j < testWidthTiles; ++j)
            {
                // get a pointer to the current tile's quad
                sf::Vertex* quad2;

                quad2 = &testArray1[(j + i * testWidthTiles) * 4];

                float x = testPosition.x + j * testTileSize.x;
                float xPlusOne = testPosition.x + (j + 1) * testTileSize.x;
                float y = testPosition.y + i * testTileSize.y;
                float yPlusOne = testPosition.y +(i + 1) * testTileSize.y;

                quad2[0].position = sf::Vector2f(x,y);
                quad2[1].position = sf::Vector2f(xPlusOne, y);
                quad2[2].position = sf::Vector2f(xPlusOne, yPlusOne );
                quad2[3].position = sf::Vector2f(x, yPlusOne);

                //compare quad center to each light in the array. If distance is less than limit, show transparent, else show black
                //Quad location in pixel space includes the x,y offset of teh viewRect
                float quadx = x+ ((xPlusOne - x ) / 2) + getViewRect().left;
                float quady = y+ ((yPlusOne - y) / 2)+ getViewRect().top;

                distance = Utility::getDistance(quadx, quady, player->getPosition().x, player->getPosition().y);
                if(distance >= distanceLimit){
                    alpha = distanceLimit; // not transparent, keep checking
                }else{
                    alpha = distance ;
                }



                /*
                for(unsigned int k = 0 ; k < lights.size() ; k++){
                    distance = Utility::getDistance(quadx, quady, lights[k].x, lights[k].y);
                    if(distance >= distanceLimit){
                        alpha = distanceLimit; // not transparent, keep checking
                    }else{
                        alpha = distance ;
                        break;
                    }
                }
                */

                int c = 15;
                quad2[0].color = sf::Color(c,c,c,alpha);
                quad2[1].color = sf::Color(c,c,c,alpha);
                quad2[2].color = sf::Color(c,c,c,alpha);
                quad2[3].color = sf::Color(c,c,c,alpha);
            }
        }
    }

    //std::cout << "Time: " << testClock.getElapsedTime().asMilliseconds() << std::endl ;
}

void AManOfMapsState::raytrace(int x0, int y0, int x1, int y1)
{
    int dx = abs(x1 - x0);
    int dy = abs(y1 - y0);
    int x = x0;
    int y = y0;
    int n = 1 + dx + dy;
    int x_inc = (x1 > x0) ? 1 : -1;
    int y_inc = (y1 > y0) ? 1 : -1;
    int error = dx - dy;
    dx *= 2;
    dy *= 2;

    for (; n > 0; --n)
    {
        testPoints.append(sf::Vertex(sf::Vector2f(x,y), sf::Color::White));
        //std::cout << "Check for Solidity: " << x << " " << y << std::endl;
        if(testRect.getGlobalBounds().contains(x,y)){

            break;
        }

        if (error > 0)
        {
            x += x_inc;
            error -= dy;
        }
        else
        {
            y += y_inc;
            error += dx;
        }
    }
}
